Skip to content

Dyno: Type-convert select-statements #27095

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

benharsh
Copy link
Member

@benharsh benharsh commented Apr 10, 2025

This PR adds initial support for type-converting select-statements using resolution info from dyno. Like production, this PR translates select-statements into if-statements.

One difference with production concerns a when-statement with multiple cases, where one of those cases is param-true. Historically we have executed a comparison for all case-exprs up to the param-true case. In this PR, we no longer execute those comparisons when any case-expr is param-true.

Testing:

  • full local
  • test/frontend/

Copy link
Contributor

@dlongnecke-cray dlongnecke-cray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks awesome, my only worry is that we should probably avoid constructing temps without types {} where possible (for now), e.g., for the storeInTempIfNeeded calls, as there are some situations where calling ->qualType() or ->typeInfo() can trigger the old resolver.

Symbol* t = nullptr;

if (qt.isUnknown()) {
t = makeNewTemp(e->qualType().getQual(), e->typeInfo());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem with this is that there are at least some paths through ->qualType() which seem to invoke the old resolver which is why I decided to only allow temps created with the chpl::types::QualifiedType. If we could figure out some way to translate without invoking the old resolver this could be done.

E.g., I know there are some primitives where the types are determined via the old resolver. I have not done due diligence but there could be more.

Maybe we hold off on adding this sort of thing if we can and if we do make sure via sanity checks/crashes that it can't actually invoke the old resolver.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was unaware that this was the case, so I'll look into using QualifiedType.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks, maybe consider adding a comment or something so that people in the future can catch on? I thought I added one but it was probably in a bad spot or too long winded.

@@ -4050,7 +4068,8 @@ void TConverter::ActualConverter::convertActual(const FormalActual& fa) {
INT_ASSERT(astActual);

// Convert the actual and leave.
std::get<Expr*>(slot) = tc_->convertExpr(astActual, rv_);
auto actualExpr = tc_->convertExpr(astActual, rv_);
std::get<Expr*>(slot) = tc_->storeInTempIfNeeded(actualExpr, {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we always need to have the actuals be temps?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The uAST here could be a Call*, and normalized production AST requires a temporary.

Copy link
Contributor

@dlongnecke-cray dlongnecke-cray Apr 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me, thanks! Maybe we could have storeInTempIfNeeded be more sensitive so it can avoid that if possible, because currently it just always stores in a temp.

@@ -5071,6 +5090,157 @@ bool TConverter::enter(const ExternBlock* node, RV& rv) {

void TConverter::exit(const ExternBlock* node, RV& rv) {}

static SymExpr* makeCaseCond(TConverter& tc, TConverter::RV& rv,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should normalize either TConverter* or TConverter& for static helper functions so that they're consistent, I don't really have a preference here but usually just do TConverter* cause then you can pass in this. Thoughts?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess I prefer to use references when possible, but for consistency I'll change this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with using references, I'd just want to go and change the other areas to use them too 😄 is all. So feel free to keep and I'll add a TODO.

@@ -4050,7 +4068,8 @@ void TConverter::ActualConverter::convertActual(const FormalActual& fa) {
INT_ASSERT(astActual);

// Convert the actual and leave.
std::get<Expr*>(slot) = tc_->convertExpr(astActual, rv_);
auto actualExpr = tc_->convertExpr(astActual, rv_);
std::get<Expr*>(slot) = tc_->storeInTempIfNeeded(actualExpr, {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the TODO, I think it would be good to have the callsites supply the types unless we're sure we're not going to accidentally invoke the old resolver.

So just get the type from convertExpr for now?

// it's a bit confusing for the case-expression's type in 'rv' to always be
// a bool.
Expr* caseExpr = tc.convertExpr(cs, rv);
caseExpr = tc.storeInTempIfNeeded(caseExpr, {});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get the type from convertExpr and pass that to storeInTempIfNeeded here?

Copy link
Contributor

@dlongnecke-cray dlongnecke-cray left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@@ -37,7 +37,13 @@ namespace resolution {

struct Resolver : BranchSensitiveVisitor<DefaultFrame> {
// types used below
using ActionAndId = std::tuple<AssociatedAction::Action, ID>;
//using ActionInfo = std::tuple<AssociatedAction::Action, ID, types::QualifiedType>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can remove now

Previously we were storing the boolean result of a when-statement's '=='
operator in the ResolvedExpression for the case-expression. This was
inconsistent with how we do things elsewhere, and caused complications
in the typed converter.

This commit adds a QualifiedType to AssociatedActions and stores the
boolean result there. Storing this result is particularly important in
the case where it is param-true or param-false.

This commit also adds a 'getAction' method to ResolvedExpression to
facilitate fetching the 'COMPARE' action.

Signed-off-by: Ben Harshbarger <[email protected]>
Also update with reviewer feedback:
- do not use 'qualType()'
- consistent usage of 'TypedConverter*'

Signed-off-by: Ben Harshbarger <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants